﻿Imports System.Net
Imports System.Text
Imports System.IO
Imports System.Threading

Public Class Form1


#Region "Variables"
    Private NumeroWorkers As Integer = 2
    Private Structure sFlooders
        Dim Nombre As String
        Dim Contador As Integer
    End Structure
    Private Flooders(NumeroWorkers) As List(Of sFlooders)

    Private FloodersALL As List(Of sFlooders)

    Private FloodersOrdenados As List(Of sFlooders)

    Private ContadorPost(NumeroWorkers) As Integer '= 0 'contador de post (en principio 15 por página)
    'Private ContadorPaginas(5) As Integer '= 1 'contador de páginas
    Private startIndex(NumeroWorkers) As Integer '= 0 'Incremento en el argumento de la URL (de 15 en 15)

    Private ContadorPostGENERAL As Integer
    Private ContadorPaginasGENERAL As Integer


    Const URL_ZONAFORO As String = "http://zonaforo.meristation.com/foros/viewtopic.php?t="

    Private THREADPost As Integer '= 1882845 '<---Título: Popularidad de los foreros
    'Private THREADPost As Integer = 1130589 '<---Hilo de los loles

    Private bFinalizado(NumeroWorkers) As Boolean '= False

    Private WithEvents TMRRefreshAllData As New Timers.Timer
    'Private WithEvents TMRRefreshAllData2 As New Timers.Timer


    Private bCompleadoWorker(NumeroWorkers) As Boolean
    Private bCancelado As Boolean

#End Region



#Region "Form Load (Punto de inicio del programa)"
    Private Sub ResetControles()
        lblTitulo.Text = ""
        lblPageNumber.Text = ""
        lblPostNumber.Text = ""
        lblFloodersCounter.Text = ""
        lblHoraInicio.Text = ""
        LblHoraFin.Text = ""
        lblStatus.Text = ""
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        tslblVersion.Text = "ver. " & Application.ProductVersion
        btnCancel.Enabled = False
        ResetControles()
    End Sub
#End Region



#Region "Obtener el código HTML de una URL"
    Private Function GetHTMLcode(ByVal direccionURL As String) As String
        Dim MyWebClient As WebClient = New WebClient()
        MyWebClient.Proxy = Nothing
        'Dim PageHTMLBytes() As Byte
        Try
            Return MyWebClient.DownloadString(direccionURL)
            'PageHTMLBytes = MyWebClient.DownloadData(direccionURL)
            ''Dim oUTF8 As UTF8Encoding = New UTF8Encoding()
            ''Return oUTF8.GetString(PageHTMLBytes)
            'Dim download As String = Encoding.UTF8.GetString(PageHTMLBytes)
            'Return download
        Catch ex As System.Exception
            Return Nothing
        End Try
    End Function
#End Region



#Region "Ver si el hilo es correcto. Se recupera el título en caso correcto"
    Private Function ThreadTopicOK(ByVal TopicT As String) As Boolean

        If TopicT.Trim = "" Then
            MessageBox.Show("Tienes que especificar el nº del topic", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Return False
        ElseIf MaskPaginaInicial.Text.Trim = "" Then
            MessageBox.Show("Tienes que especificar la página incial", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Return False
        ElseIf Int32.Parse(MaskPaginaInicial.Text.Trim) = 0 Then
            MaskPaginaInicial.Text = "1"
        Else
            Try
                Dim s As String = GetHTMLcode(URL_ZONAFORO & TopicT.Trim)
                If Not IsNothing(s) Then
                    Dim sd() As String = s.Split(vbLf)
                    'M3:
                    '<td class="celda6" bgcolor="#e9e9e9" valign="top" align="left" style="border-right:1px solid #cccccc;"><font face="Verdana, Arial, Helvetica, sans-serif" size="2" color="#666666"><b>Popularidad de los foreros</b></font></td>

                    'M4:
                    '<title>Ver tema - [ROL] SKC: El Legado (ACTU 10/130: La ceremonia del nombramiento: Decisión Final) :: ZonaForo de MERISTATION</title>

                    'M3:
                    'Dim ParseTITULO As String = "<font face=""Verdana, Arial, Helvetica, sans-serif"" size=""2"" color=""#666666""><b>"
                    'Dim ParseTITULOfin As String = "</b></font></td>"

                    'M4:
                    Dim ParseTITULO As String = "title=""Ver tema"

                    Dim ParseTITULOfin As String = " | Foro Meristation</title>"

                    Dim sTitulo As String = ""

                    'M3:
                    'Dim StartParsing As Integer = 290

                    'M4
                    Dim StartParsing As Integer = 8 '9


                    For i As Integer = StartParsing To sd.Length - 1
                        Dim p As Integer = sd(i).Trim.LastIndexOf(ParseTITULO)
                        If p <> -1 Then
                            sTitulo = sd(i + 1).Trim.Substring(7) '.Substring(p + ParseTITULO.Length)
                            sTitulo = sTitulo.Replace(ParseTITULOfin, "")                            
                            Exit For
                        End If
                    Next
                    If sTitulo <> "" Then
                        'El hilo parece correcto
                        sTitulo = sTitulo.Replace("&quot;", "''")
                        sTitulo = sTitulo.Replace("&gt;", ">")
                        sTitulo = sTitulo.Replace("&lt;", "<")
                        sTitulo = sTitulo.Replace("&amp;", "&")
                        lblTitulo.Text = sTitulo
                        Return True
                    Else
                        MessageBox.Show("No se ha podido encontrar el título del post especificado")
                        Return False
                    End If
                Else
                    MessageBox.Show("El topic no existe o no es correcto")
                    Return False
                End If
            Catch ex As Exception
                MessageBox.Show("El topic no es correcto")
                Return False
            End Try
        End If
    End Function
#End Region



#Region "Generación del fichero con los resultados"
    ''' <summary>
    ''' método que genera el fichero de texto con los resultados
    ''' el fichero se guarda en la misma ruta que el ejecutable y con el nombre
    ''' meriflooders_T[numeroDelHilo].txt
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub GuardandoFicheros()
        Try
            Dim sFileName As String = Application.StartupPath & "\meriflooders_T" & THREADPost & ".txt"

            Dim objEscritor = New IO.StreamWriter(sFileName, False, System.Text.Encoding.Default)
            objEscritor.WriteLine("[code]")
            objEscritor.WriteLine("::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::")
            objEscritor.WriteLine("Fichero generado por FloodersInspector v" & Application.ProductVersion & " (CyS -t2093049 [29/05/2013] Compatible)")
            objEscritor.WriteLine("::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::")
            objEscritor.WriteLine("")
            objEscritor.WriteLine("Estadisiticas:                 viewtopic.php?t=" & THREADPost)
            objEscritor.WriteLine("Título: " & lblTitulo.Text)
            objEscritor.WriteLine("Nº de post evaluados:          " & ContadorPostGENERAL)
            objEscritor.WriteLine("Nº de páginas evaluadas:       " & ContadorPaginasGENERAL)
            If Int32.Parse(MaskPaginaInicial.Text.Trim) <> 1 Then
                objEscritor.WriteLine("         Página inicial:       " & MaskPaginaInicial.Text.Trim)
            End If
            objEscritor.WriteLine("")
            objEscritor.WriteLine("Hora Incio Scan:               " & lblHoraInicio.Text)
            objEscritor.WriteLine("Hora Fin Scan:                 " & LblHoraFin.Text)
            objEscritor.WriteLine("")
            objEscritor.WriteLine("::::::::::::::::::::::::::: HALL OF BLAME ::::::::::::::::::::::::::::")
            objEscritor.WriteLine("")
            objEscritor.WriteLine("    forero                       Post en este hilo ( porcentaje )")
            objEscritor.WriteLine("")
            Dim iCnt As Integer = 0
            Dim sDt As String = ""
            For Each iFlooder As sFlooders In FloodersOrdenados

                sDt = ""
                iCnt += 1
                'numero:
                sDt = iCnt.ToString.PadLeft(3, " ")
                'Nombre del flooder ..... contador
                sDt &= ":" & iFlooder.Nombre.PadRight(43, ".") & iFlooder.Contador.ToString.PadLeft(3, " ")
                'cálculo del porcentaje
                Dim porcentaje As String = Format((iFlooder.Contador / ContadorPostGENERAL) * 100, "0.000")
                sDt &= " (" & porcentaje.ToString.PadLeft(6, " ") & " %)"
                objEscritor.WriteLine(sDt)

            Next

            objEscritor.WriteLine("[/code]")
            objEscritor.Flush()
            objEscritor.Close()

            MessageBox.Show("Se ha generado el fichero " & sFileName, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information)
        Catch ex As Exception
            MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try

    End Sub
#End Region



#Region "Botón Cancelar"
    Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
        If MessageBox.Show("¿Estas seguro que quieres cancelar el proceso?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then

            BackgroundWorker1.CancelAsync()
            BackgroundWorker2.CancelAsync()

        End If
    End Sub
#End Region

#Region "Botón GO"
    Private Sub btnGo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGo.Click
        ResetControles()

        lblStatus.Text = "Analizando topic. Por favor, espera..."
        lblStatus.Refresh()

        If ThreadTopicOK(MaskThreadTopic.Text) Then

            THREADPost = Int32.Parse(MaskThreadTopic.Text.Trim)

            btnCancel.Enabled = True
            btnGo.Enabled = False
            MaskThreadTopic.Enabled = False
            MaskPaginaInicial.Enabled = False

            For i As Integer = 1 To NumeroWorkers
                Flooders(i) = New List(Of sFlooders)
                ContadorPost(i) = 0
                startIndex(i) = ((i * 15) - 15) + (Int32.Parse(MaskPaginaInicial.Text.Trim) * 15) - 15
                bFinalizado(i) = False
                bCompleadoWorker(i) = False
            Next

            FloodersALL = New List(Of sFlooders)

            ContadorPostGENERAL = 0

            lblHoraInicio.Text = Now
            LblHoraFin.Text = ""

            lblFloodersCounter.Text = ""

            lblStatus.Text = "Parseando. Por favor, espera..."

            bCancelado = False
            'bAllCompletedC = False

            Label1.Visible = False
            lblFloodersCounter.Visible = False


            TMRRefreshAllData.Interval = 1000
            TMRRefreshAllData.Enabled = True

            'esto impide que se llame al BackgroundWorked otra vez hasta que se haya finalizado o cancelado
            '(Otra opción es poner este boton 'disable' hasta que se complete o cancele la operación)

            'If Not BackgroundWorker1.IsBusy Then
            'se inicia el 'hilo'
            BackgroundWorker1.RunWorkerAsync()
            'End If

            BackgroundWorker2.RunWorkerAsync()

        Else
            ResetControles()
            lblStatus.Refresh()
        End If
    End Sub
#End Region




#Region "Buscar Flooders"

    ''' <summary>
    ''' Proceso que lanza del backgroundworker
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function BuscarFlooders(ByVal Indice As Integer) As Integer
        Dim lRet As Integer = 1

        Do
            DoProcess(Indice)

            If BackgroundWorker1.CancellationPending Then
                bFinalizado(Indice) = True
                lRet = -1
            End If

            If BackgroundWorker2.CancellationPending Then
                bFinalizado(Indice) = True
                lRet = -1
            End If

        Loop Until bFinalizado(Indice) = True

        Return lRet
    End Function

    ''' <summary>
    ''' Rutina que incrementa la URL y lanza el parseo de la página
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub DoProcess(ByVal Indice As Integer)
        CheckForIllegalCrossThreadCalls = False

        Dim s As String = ""
        Dim urlParse As String = URL_ZONAFORO & THREADPost & "&postday=0&postorder=asc&start=" & startIndex(Indice)

        s = GetHTMLcode(urlParse)

        If Not IsNothing(s) Then
            ParseHTMLCode(s, Indice) '<---- La madre del cordero está aquí

            startIndex(Indice) += (15 * NumeroWorkers) '75 '15*5

        Else
            'Esto no debería ocurrir nunca!!!!
            Debug.Print(TimeOfDay.ToString & "---> Nothing en GetHTMCode(" & urlParse & ")")
            'erroresCNT += 1
        End If
    End Sub

#Region "       Rutina que parsea la URL y guarda la información en la lista generica Flooders"

    'M3:
    'class="msnoticiastitulo" style="color:#000066;">javi_930</a><br /></span></div>
    'M4: No se muestran avatares:
    '<div><a href="http://club.meristation.com/es/user/370475" class="msnoticiastitulo" ">El_Peto</a><br /></span></div>
    'M4: logeado con avatares:
    '<div><img src="images/avatars/111/11177435104f4b84ee4822e.jpg" alt="" border="0" /><br /><a href="http://club.meristation.com/es/user/370475" class="msnoticiastitulo" ">El_Peto</a><br /></span></div>
    'M4: rol Moderatroll:
    '<div><img src="images/avatars/203/20360780804f4d3d5957231.png" alt="" border="0" /><br /><a href="http://club.meristation.com/es/user/462836" class="msnoticiastitulo" ">Salvor Hardin</a><br /><a class="rolemoderator" href="rules.php"><strong class="genmed rolemoderator">Moderador</strong></a><br /></span></div>



    'M3:
    'Const PARSE_strUserINICIO As String = "class=""msnoticiastitulo"" style=""color:#000066;"">"
    'M4:
    'Const PARSE_strUserINICIO As String = "class=""msnoticiastitulo"" "">"
    Const PARSE_strUserINICIO As String = "class=""msnoticiastitulo "">"
    'Const PARSE_strUserFIN As String = "</a><br /></span></div>"
    Const PARSE_strUserFIN As String = "</a><br /></div>"

    Const PARSE_FueraRango As String = "Could not obtain post index information.</div>"
    Const PARSE_FueraRango2 As String = "Could not obtain post/user information.</div>"
    Const PARSE_FueraRango3 As String = ">No existen mensajes para este tema</div>"
    'M3:
    'Const PARSE_strFINpost As String = "<table class=""tabla_paginasforo"" style=""border-top-width:1px;"""
    'M4:
    Const PARSE_strFINpost As String = "<div id=""footer"">"

    Const REPLACE_strNOMBRESraros As String = "<span style=""text-decoration:underline;""> </span>"
    Const REPLACE_strRole As String = "</a><br /><a class=""role"

    Private ParseLineFROM As Integer = 230

    Private Sub ParseHTMLCode(ByVal htmlCode As String, ByVal Indice As Integer)

        Dim sD() As String = htmlCode.Split(vbLf)
        For i As Integer = ParseLineFROM To sD.Length - 1

            If sD(i).LastIndexOf(PARSE_strUserINICIO) <> -1 Then
                ''''''''ContadorPost(Indice) += 1
                Dim p1 = sD(i).Trim.LastIndexOf(PARSE_strUserINICIO)

                Dim sUser As String = sD(i).Trim.Substring(p1 + PARSE_strUserINICIO.Length)
                sUser = sUser.Replace(PARSE_strUserFIN, "")

                If sUser.Contains(REPLACE_strNOMBRESraros) Then
                    sUser = sUser.Replace(REPLACE_strNOMBRESraros, "_")
                ElseIf sUser.Contains(REPLACE_strRole) Then
                    Dim iMassReplace As Integer = sUser.LastIndexOf(REPLACE_strRole)
                    sUser = sUser.Substring(0, iMassReplace)
                End If

                'Dim iX As Integer = sUser.LastIndexOf(Chr(34))
                'sUser = sUser.Substring(iX + 2)

                'Se mira si un flooder ya está en la lista genérica
                Dim itemEnLista As sFlooders = Flooders(Indice).Find(Function(s) s.Nombre = (sUser))

                If IsNothing(itemEnLista.Nombre) Then
                    'El flooder no está en la lista
                    itemEnLista.Nombre = sUser
                    itemEnLista.Contador = 1
                    Flooders(Indice).Add(itemEnLista)
                Else
                    'El flooder ya estaba en la lista. Incrementar el contador en 1

                    'interesante!!!
                    'http://stackoverflow.com/questions/414981/directly-modifying-listt-elements
                    'http://stackoverflow.com/questions/51526/changing-the-value-of-an-element-in-a-list-of-structs

                    Dim nitem As sFlooders
                    nitem = itemEnLista
                    Flooders(Indice).Remove(itemEnLista)

                    nitem.Contador += 1
                    Flooders(Indice).Add(nitem)
                End If
            ElseIf sD(i).LastIndexOf(PARSE_strFINpost) <> -1 Then
                'fin del parse
                ContadorPost(Indice) += 15
                Exit For
            ElseIf sD(i).LastIndexOf(PARSE_FueraRango) <> -1 Then
                bFinalizado(Indice) = True
                Exit For
            ElseIf sD(i).LastIndexOf(PARSE_FueraRango2) <> -1 Then
                bFinalizado(Indice) = True
                Exit For
            ElseIf sD(i).LastIndexOf(PARSE_FueraRango3) <> -1 Then
                bFinalizado(Indice) = True
                Exit For
            End If
        Next
    End Sub

#End Region

#End Region

#Region "'Eventos' del Backgroundworker"
    Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        e.Result = BuscarFlooders(1)
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        If e.Result = -1 Then
            bCancelado = True
        Else
            bCompleadoWorker(1) = True
        End If
    End Sub

    Private Sub BackgroundWorker2_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker2.DoWork
        e.Result = BuscarFlooders(2)
    End Sub

    Private Sub BackgroundWorker2_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker2.RunWorkerCompleted
        If e.Result = -1 Then
            bCancelado = True
        Else
            bCompleadoWorker(2) = True
        End If
    End Sub

#End Region

#Region "ToolStatusBar"
    Private Sub ToolStripStatusLabel1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripStatusLabel1.Click
        Try
            Process.Start(ToolStripStatusLabel1.Text)
        Catch ex As Exception

        End Try
    End Sub

    Private Sub ToolStripStatusLabel1_MouseEnter(ByVal sender As Object, ByVal e As System.EventArgs) Handles ToolStripStatusLabel1.MouseEnter
        Cursor = Cursors.Hand
    End Sub

    Private Sub ToolStripStatusLabel1_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles ToolStripStatusLabel1.MouseLeave
        Cursor = Cursors.Default
    End Sub
#End Region


    Private Sub TMRRefreshAllData_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles TMRRefreshAllData.Elapsed
        If bCancelado Then
            TMRRefreshAllData.Enabled = False
            lblStatus.Text = "Proceso cancelado"

            btnCancel.Enabled = False
            btnGo.Enabled = True
            MaskThreadTopic.Enabled = True
            MaskPaginaInicial.Enabled = True

        Else

            Dim bAllCompleted As Boolean = True
            For i As Integer = 1 To NumeroWorkers
                If bCompleadoWorker(i) = False Then
                    bAllCompleted = False
                    Exit For
                End If
            Next

            ContadorPostGENERAL = 0

            For i As Integer = 1 To NumeroWorkers
                ContadorPostGENERAL += ContadorPost(i)
            Next

            ContadorPaginasGENERAL = (ContadorPostGENERAL \ 15) + 1
            lblPageNumber.Text = Int32.Parse(MaskPaginaInicial.Text.Trim) - 1 + ContadorPaginasGENERAL
            lblPostNumber.Text = ContadorPostGENERAL

            If bAllCompleted Then

                If ContadorPostGENERAL Mod 15 = 0 Then
                    ContadorPaginasGENERAL = (ContadorPostGENERAL \ 15)
                Else
                    ContadorPaginasGENERAL = (ContadorPostGENERAL \ 15) + 1
                End If
                lblPageNumber.Text = Int32.Parse(MaskPaginaInicial.Text.Trim) - 1 + ContadorPaginasGENERAL
                TMRRefreshAllData.Enabled = False


                lblStatus.Text = "Ordenando Flooders de mayor a menor. Por favor, espera..."
                lblStatus.Refresh()

                FloodersALL.Clear()
                For Each sItem As sFlooders In Flooders(1)
                    FloodersALL.Add(sItem)
                Next

                For i As Integer = 2 To NumeroWorkers
                    If Flooders(i).Count > 0 Then
                        For Each sItem As sFlooders In Flooders(i)
                            Dim itemEnListaALL As sFlooders = FloodersALL.Find(Function(s) s.Nombre = sItem.Nombre)
                            If IsNothing(itemEnListaALL.Nombre) Then
                                itemEnListaALL.Nombre = sItem.Nombre
                                itemEnListaALL.Contador = 1
                                FloodersALL.Add(itemEnListaALL)

                            Else
                                Dim nitem As sFlooders
                                nitem = itemEnListaALL
                                FloodersALL.Remove(itemEnListaALL)

                                nitem.Contador += sItem.Contador
                                FloodersALL.Add(nitem)
                            End If

                        Next
                    End If
                Next

                Label1.Visible = True
                lblFloodersCounter.Visible = True

                lblFloodersCounter.Text = FloodersALL.Count

                'ordenar la lista primero por número de post (descendentemente) y luego por nombre
                FloodersOrdenados = New List(Of sFlooders)
                FloodersOrdenados = FloodersALL.OrderByDescending(Function(c) c.Contador).ThenBy(Function(c) c.Nombre).ToList()

                lblStatus.Text = "Guardando fichero. Por favor, espera..."
                lblStatus.Refresh()

                LblHoraFin.Text = Now

                'guardar los resultados en un fichero de texto 
                GuardandoFicheros()

                lblStatus.Text = "Proceso finalizado"

                btnCancel.Enabled = False
                btnGo.Enabled = True
                MaskThreadTopic.Enabled = True
                MaskPaginaInicial.Enabled = True

                Try
                    Process.Start(Application.StartupPath & "\meriflooders_T" & THREADPost & ".txt")
                Catch ex As Exception

                End Try

            End If

        End If

    End Sub

End Class
